#include "stdafx.h"
#include "3DModels.h"
#include "ifcengine.h"
#include "citygml.h"

#include "d3d8.h"
#include "d3dx8.h"
#define	 D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_NORMAL)

CIfcModel::CIfcModel()
{
	TRACE("CIfcModel::CIfcModel");
	m_ref       = 0; 
	m_URI       = NULL;
    m_filePath  = NULL;
    m_fileName  = NULL;
	m_type      = MT_IFC;
	m_p3DObject = NULL; 
	
    m_minBB.x   = 0.0;
    m_minBB.y   = 0.0;
    m_minBB.z   = 0.0;
    m_maxBB.x   = 0.0;
    m_maxBB.y   = 0.0;
    m_maxBB.z   = 0.0;

	m_nrIndices  = 0;
	m_nrVertices = 0;
	m_pIndices   = NULL;
	m_pVertices  = NULL;

	m_unitScale  = 1.0;
}


void CIfcModel::OpenModel()
{
	TRACE("CIfcModel::OpenModel");
	m_ref = sdaiOpenModelBN(0, m_URI, m_schemaFile);

	if (m_ref)
	{
//		setPostProcessing(m_ref, true);
		Add3DObjects();
	}
	else
		AfxMessageBox("Failed opening the model");
}


void CIfcModel::CloseModel()
{
	TRACE("CIfcModel::CloseModel");

	if (m_URI != NULL)
		delete[] m_URI;
    if(m_filePath != NULL)
		delete[] m_filePath;
    if(m_fileName != NULL)
		delete[] m_fileName;
	if (m_p3DObject != NULL)
		Del3DObjects(m_p3DObject);

	if (m_pIndices != NULL)
		delete[] m_pIndices;
	if (m_pVertices != NULL)
		delete[] m_pVertices;

	if (m_ref != 0)
		sdaiCloseModel(m_ref);    
}


void CIfcModel::Del3DObjects(S3DObject *object)
{
	TRACE("CIfcModel::Del3DObjects");

	S3DObject *tmp = object;
	// find the next object with a type differing from the current 
	// memory was allocated grouped by types and has to be freed
	// in similar batches.
	while (tmp && object->type == tmp->type)
		tmp = tmp->next;

	// there is a next object with a different type, delete this first
	if (tmp != NULL)
		Del3DObjects(tmp);

	// delete the batch of objects
	delete[] object->polygon;
//	delete[] object->name;
	delete[] object;
}


void CIfcModel::GetUnitScale()
{
	m_unitScale = 1.0;

	// Get the unit scaling factor (convert to meters)
	int *unitAssAggr = sdaiGetEntityExtentBN(m_ref, "IFCUNITASSIGNMENT");

	// there should always be one unit assignment entity in a file
	ASSERT(sdaiGetMemberCount(unitAssAggr) == 1);
	if (unitAssAggr)
	{
		int SIUNIT_TYPE   = sdaiGetEntity(m_ref, "IFCSIUNIT");
		int CONVUNIT_TYPE = sdaiGetEntity(m_ref, "IFCCONVERSIONBASEDUNIT"); 
		int unitAssInst, unitsAggr, nrUnits, unitInst;
		engiGetAggrElement(unitAssAggr, 0, sdaiINSTANCE, &unitAssInst);
		sdaiGetAttrBN(unitAssInst, "Units", sdaiAGGR, &unitsAggr); 
		
		nrUnits = sdaiGetMemberCount((int*)unitsAggr);
		for (int i = 0; i < nrUnits; i++)
		{
			char *type, *val;
			engiGetAggrElement((int*)unitsAggr, i, sdaiINSTANCE, &unitInst);
			sdaiGetAttrBN(unitInst, "UnitType", sdaiSTRING, &type);

			if (strncmp(type, ".LENGTHUNIT.", 12) == 0)
			{
				//found measure
				if  (sdaiGetInstanceType(unitInst) == SIUNIT_TYPE) 
				{
					// Get the scale from the prefix
					sdaiGetAttrBN(unitInst, "Prefix", sdaiSTRING, &val);
					if (val == NULL) break; //scale = 1
					if (strncmp(val, ".MILLI.", 7) == 0)       m_unitScale = 0.001;
					else if (strncmp(val, ".CENTI.", 7) == 0)  m_unitScale = 0.01;
					else if (strncmp(val, ".DECI.", 6) == 0)   m_unitScale = 0.1;
					else if (strncmp(val, ".DECA.", 6) == 0)   m_unitScale = 10.0;
					else if (strncmp(val, ".HECTO.", 7) == 0)  m_unitScale = 100.0;
					else if (strncmp(val, ".KILO.", 6) == 0)   m_unitScale = 1000.0;
				}
				else if  (sdaiGetInstanceType(unitInst) == CONVUNIT_TYPE) 
				{
					//get the scale from the not SI type
					sdaiGetAttrBN(unitInst, "Name", sdaiSTRING, &val);
					if (val == NULL) break; //scale = 1
					if (strncmp(val, "inch", 4) == 0)       m_unitScale = 0.0254;
					else if (strncmp(val, "foot", 4) == 0)  m_unitScale = 0.3048;
					else if (strncmp(val, "yard", 4) == 0)  m_unitScale = 0.9144;
					else if (strncmp(val, "mile", 4) == 0)  m_unitScale = 1609.344;
				}				
				break;
			}
		}
	}
}

void CIfcModel::GetBoundingBox()
{
	// determine the object minimum and maximum
	if (m_nrVertices > 0)
	{
		int i = 1;
		m_minBB.x = m_maxBB.x = (((CUSTOMVERTEX *) m_pVertices)[0]).x; 
		m_minBB.y = m_maxBB.y = (((CUSTOMVERTEX *) m_pVertices)[0]).y; 
		m_minBB.z = m_maxBB.z = (((CUSTOMVERTEX *) m_pVertices)[0]).z; 
		while  (i < m_nrVertices) 
		{
			if ((((CUSTOMVERTEX *) m_pVertices)[i]).x > m_maxBB.x)      m_maxBB.x = (((CUSTOMVERTEX *) m_pVertices)[i]).x; 
			else if ((((CUSTOMVERTEX *) m_pVertices)[i]).x < m_minBB.x) m_minBB.x = (((CUSTOMVERTEX *) m_pVertices)[i]).x; 
			if ((((CUSTOMVERTEX *) m_pVertices)[i]).y > m_maxBB.y)      m_maxBB.y = (((CUSTOMVERTEX *) m_pVertices)[i]).y; 
			else if ((((CUSTOMVERTEX *) m_pVertices)[i]).y < m_minBB.y) m_minBB.y = (((CUSTOMVERTEX *) m_pVertices)[i]).y; 
			if ((((CUSTOMVERTEX *) m_pVertices)[i]).z > m_maxBB.z)      m_maxBB.z = (((CUSTOMVERTEX *) m_pVertices)[i]).z; 
			else if ((((CUSTOMVERTEX *) m_pVertices)[i]).z < m_minBB.z) m_minBB.z = (((CUSTOMVERTEX *) m_pVertices)[i]).z; 
			i++;
		}

		m_minBB.x *=  m_unitScale;
		m_maxBB.x *=  m_unitScale;
		m_minBB.y *=  m_unitScale;
		m_maxBB.y *=  m_unitScale;
		m_minBB.z *=  m_unitScale;
		m_maxBB.z *=  m_unitScale;
	}
}

void CIfcModel::Add3DObjects()
{
	TRACE("CIfcModel::Add3DObjects");
//	Add3DObjectsByType("IFCANNOTATION", OT_ANNOTATION, 20, 0, 0, 255);
	Add3DObjectsByType("IFCBEAM",                     OT_BEAM,                     210, 210, 210, 255);
	Add3DObjectsByType("IFCBUILDINGELEMENTCOMPONENT", OT_BUILDINGELEMENTCOMPONENT, 120,  10, 120, 255);
	Add3DObjectsByType("IFCBUILDINGELEMENTPROXY",     OT_BUILDINGELEMENTPROXY,      65,  10, 120, 255);
	Add3DObjectsByType("IFCCOLUMN",                   OT_COLUMN,                   210, 210, 210, 255);
	Add3DObjectsByType("IFCCOVERING",                 OT_COVERING,                  20,   0,   0, 255);
	Add3DObjectsByType("IFCCURTAINWALL",              OT_CURTAINWALL,               20, 180,   0, 255);
	Add3DObjectsByType("IFCDOOR",                     OT_DOOR,                     100,  40,  35, 255);
	Add3DObjectsByType("IFCELEMENTASSEMBLY",          OT_ELEMENTASSEMBLY,           20,   0,   0, 255);
	Add3DObjectsByType("IFCFASTENER",                 OT_FASTENER,                  20,   0,   0, 255);
	Add3DObjectsByType("IFCFOOTING",                  OT_FOOTING,                   20,   0,   0, 255);
	Add3DObjectsByType("IFCMECHANICALFASTENER",       OT_MECHANICALFASTENER,        20,   0,   0, 255);
	Add3DObjectsByType("IFCMEMBER",                   OT_MEMBER,                    20,   0,   0, 255);
	Add3DObjectsByType("IFCPILE",                     OT_PILE,                      20,   0,   0, 255);
	Add3DObjectsByType("IFCPLATE",                    OT_PLATE,                     20,   0,   0, 255);
	Add3DObjectsByType("IFCRAILING",                  OT_RAILING,                   20,  20,  20, 255);
	Add3DObjectsByType("IFCRAMP",                     OT_RAMP,                      20,   0,   0, 255);
	Add3DObjectsByType("IFCRAMPFLIGHT",               OT_RAMPFLIGHT,                20,   0,   0, 255);
	Add3DObjectsByType("IFCROOF",                     OT_ROOF,                     140,  10,  30, 255);
	Add3DObjectsByType("IFCSITE",                     OT_SITE,                      80,  50,  10, 255);
	Add3DObjectsByType("IFCSLAB",                     OT_SLAB,                     140,  10,  30, 255);
	Add3DObjectsByType("IFCSPACE",                    OT_SPACE,                     20,  20,  50,  10);
	Add3DObjectsByType("IFCSTAIR",                    OT_STAIR,                    180, 180,  20, 255);
	Add3DObjectsByType("IFCSTAIRFLIGHT",              OT_STAIRFLIGHT,              180, 180,  20, 255);
	Add3DObjectsByType("IFCSTRUCTURALCURVEMEMBER",    OT_STRUCTURALCURVEMEMBER,     20,   0,   0, 255);
	Add3DObjectsByType("IFCWALL",                     OT_WALL,                     200,  80,  25, 255);
	Add3DObjectsByType("IFCWALLSTANDARDCASE",         OT_WALL,                     200,  80,  25, 255);
	Add3DObjectsByType("IFCWINDOW",                   OT_WINDOW,                     0, 255, 255,  25);
	Add3DObjectsByType("IFCAIRTERMINAL",              OT_AIRTERMINAL,               20,   0,   0, 255);
	Add3DObjectsByType("IFCDISTRIBUTIONELEMENT",      OT_DISTRIBUTIONELEMENT,       20,   0,   0, 255);
	Add3DObjectsByType("IFCFURNISHINGELEMENT",        OT_FURNISHINGELEMENT,         20,   0,   0, 255);
	Add3DObjectsByType("IFCFLOWTERMINAL",             OT_FLOWTERMINAL,              20,   0,   0, 255);
	Add3DObjectsByType("IFCFLOWFITTING",              OT_FLOWFITTING,               20,   0,   0, 255);
	Add3DObjectsByType("IFCFLOWSEGMENT",              OT_FLOWSEGMENT,               20,   0,   0, 255);
	Add3DObjectsByType("IFCFLOWCONTROLLER",           OT_FLOWCONTROLLER,            20,   0,   0, 255);
	Add3DObjectsByType("IFCGRID",                     OT_GRID,                      20,   0,   0, 255);

	if	(m_p3DObject) 
	{
		//	We need to build up the 3D structure in the IFC Engine DLL to retrieve geometrical data
		initializeModelling(m_ref, &m_nrVertices, &m_nrIndices, 0);
		m_pIndices  = new int[m_nrIndices];
		m_pVertices = new CUSTOMVERTEX[m_nrVertices];
		finalizeModelling(m_ref, (float*)m_pVertices, m_pIndices, D3DFVF_CUSTOMVERTEX);

		S3DObject	*object = m_p3DObject;
		while  (object) 
		{
			getInstanceInModelling(m_ref, object->ref, 1, &object->polygon->startVertex, 
				                   &object->polygon->startIndex, &object->polygon->primitiveCount);
			object = object->next;
		}

		GetUnitScale();
		GetBoundingBox();
	}
}

void CIfcModel::Add3DObjectsByType(char * ObjectSPFFName, ObjectType type, float r, float g, float b, float alpha)
{
	TRACE("CIfcModel::Fill3DObjectGroup");

	S3DObject **object = &m_p3DObject;
	SPolygon  *poly = NULL;
	int			 objectInst, *objectAggr, nrObjectsInAggr;
	char		 * string;

	objectAggr = sdaiGetEntityExtentBN(m_ref, ObjectSPFFName);
	nrObjectsInAggr = sdaiGetMemberCount(objectAggr);
	if	(nrObjectsInAggr) 
    {
		// get the last 3dObject
        while (*object != NULL)
            object = &((*object)->next);
        (*object) = new S3DObject[nrObjectsInAggr];
		poly = new SPolygon[nrObjectsInAggr];
	
        //add each individual object
		for (int i = 0; i < nrObjectsInAggr; ++i) 
        {
			engiGetAggrElement(objectAggr, i, sdaiINSTANCE, &objectInst);

            (*object)[i].ref = objectInst;
			(*object)[i].type = type;
			
			sdaiGetAttrBN(objectInst, "Name", sdaiSTRING, &(*object)[i].name);
			if	((*object)[i].name == NULL) 
			{
				(*object)[i].name = new char[10];
		        strcpy((*object)[i].name, "<no name>");
			}
	        
			(*object)[i].polygon = &(poly[i]);
            (*object)[i].polygon->mtrl = gMaterialManager.AddMaterial(r, g, b, alpha);
//			(*object)[i].polygon->next = NULL;
			(*object)[i].visible = true;

			if (i + 1 < nrObjectsInAggr)
				(*object)[i].next = &((*object)[i + 1]);
			else
	            (*object)[i].next = NULL;
		}
	}
}


/*
ObjectTypes CIfcModel::GetObjectType(int *instance)
{
	int localId;
	char *name; 
	engiGetInstanceMetaInfo(instance, &localId, &name, NULL);

	if (strcmp(name, "IfcProject") == 0)		return OT_PROJECT;
	if (strcmp(name, "IfcSite") == 0)			return OT_SITE;
	if (strcmp(name, "IfcBuilding") == 0)		return OT_BUILDING;
	if (strcmp(name, "IfcStorey") == 0)			return OT_STOREY;
	if (strcmp(name, "IfcSpace") == 0)			return OT_SPACE;

/*
//	OT_ANNOTATION,
	OT_BEAM,
	OT_BUILDINGELEMENTCOMPONENT,
	OT_BUILDINGELEMENTPROXY,
	OT_COLUMN, 

	OT_COVERING,
	OT_CURTAINWALL,
	OT_DOOR,
	OT_ELEMENTASSEMBLY,
	OT_FASTENER,
	OT_FOOTING,
	OT_MECHANICALFASTENER,
	OT_MEMBER,
	OT_PILE,
	OT_PLATE,
	OT_RAILING,
	OT_RAMP,
	OT_RAMPFLIGHT,
	OT_ROOF,
	OT_SLAB,
	OT_SPACE,
	OT_STAIR,
	OT_STAIRFLIGHT,
	OT_STRUCTURALCURVEMEMBER,
	OT_WALL,
	OT_WINDOW,
	OT_AIRTERMINAL,
	OT_DISTRIBUTIONELEMENT,
	OT_FURNISHINGELEMENT,
	OT_FLOWTERMINAL,
	OT_FLOWFITTING,
	OT_FLOWSEGMENT,
	OT_FLOWCONTROLLER,
	OT_GRID,
*
	return UNKNOWN;
}

void CIfcModel::GetObjects(SIfcObject *parent)
{
    int relaggregates, relaggregate, objects;
	SIfcObject *lastChild, **allocChild;
    
    // decomposition childs
    sdaiGetAttrBN(parent->id, "IsDecomposedBy", sdaiAGGR, &relaggregates); 
    int nrRelaggregates = sdaiGetMemberCount((int*)relaggregates);

	allocChild = &parent->child;
    for (int i = 0; i < nrRelaggregates; i++)
    {
        engiGetAggrElement((int*)relaggregates, i, sdaiINSTANCE, &relaggregate);
        sdaiGetAttrBN(relaggregate, "RelatedObjects", sdaiAGGR, &objects);

        int nrObjects = sdaiGetMemberCount((int*)objects);
		if (nrObjects > 0)
			*allocChild = new SIfcObject[nrObjects];

		lastChild = *allocChild;
		for (int n = 0; n < nrObjects; n++)
        {
			engiGetAggrElement((int*)objects, n, sdaiINSTANCE, &lastChild->id);
            sdaiGetAttrBN(lastChild->id, "Name", sdaiSTRING, &lastChild->name);       
			lastChild->type = GetObjectType(&lastChild->id);
			lastChild->primitiveCount = 0;
			lastChild->startIndex = 0;
			lastChild->startVertex = 0;
			lastChild->next = NULL;
			lastChild->child = NULL;

			GetObjects(lastChild);

			if (n < nrObjects - 1)
			{
				lastChild->next = &parent->child[n + 1]; 
				lastChild = lastChild->next;
			}
        }
		allocChild = &lastChild->next;
    }

    // object childs
    sdaiGetAttrBN(parent->id, "ContainsElements", sdaiAGGR, &relaggregates); 
    nrRelaggregates = sdaiGetMemberCount((int*)relaggregates);

    for (int i = 0; i < nrRelaggregates; i++)
    {
        engiGetAggrElement((int*)relaggregates, i, sdaiINSTANCE, &relaggregate);
        sdaiGetAttrBN(relaggregate, "RelatedElements", sdaiAGGR, &objects);

        int nrObjects = sdaiGetMemberCount((int*)objects);
		if (nrObjects > 0)
			*allocChild = new SIfcObject[nrObjects];

		lastChild = *allocChild;
		for (int n = 0; n < nrObjects; n++)
        {
			engiGetAggrElement((int*)objects, n, sdaiINSTANCE, &lastChild->id);
            sdaiGetAttrBN(lastChild->id, "Name", sdaiSTRING, &lastChild->name);       
			lastChild->type = GetObjectType(&lastChild->id);
			lastChild->primitiveCount = 0;
			lastChild->startIndex = 0;
			lastChild->startVertex = 0;
			lastChild->next = NULL;
			lastChild->child = NULL;

			GetObjects(lastChild);

			if (n < nrObjects - 1)
			{
				lastChild->next = &parent->child[n + 1]; 
				lastChild = lastChild->next;
			}
        }
		allocChild = &lastChild->next;
    }
}

void CIfcModel::GetObjects()
{
	// Get the project object
    int *projectAggr;

	projectAggr = sdaiGetEntityExtentBN(m_modelRef, "IFCPROJECT");

    int nrProjectsInAggr = sdaiGetMemberCount(projectAggr);
	if (sdaiGetMemberCount(projectAggr) > 1)
		AfxMessageBox("Ifc-file contains more than 1 IFCPROJECT");

	if (sdaiGetMemberCount(projectAggr) > 0)
	{
		m_rootObject.type = OT_PROJECT;
		engiGetAggrElement(projectAggr, 0, sdaiINSTANCE, &m_rootObject.id);
		sdaiGetAttrBN(m_rootObject.id, "Name", sdaiSTRING, &m_rootObject.name);
		m_rootObject.mtrl = 0;
		m_rootObject.primitiveCount = 0;
		m_rootObject.startIndex = 0;
		m_rootObject.startVertex = 0;
		m_rootObject.visible = true;
		m_rootObject.child = NULL;
		m_rootObject.next = NULL;

		int relaggregates, relaggregate, objects;
		SIfcObject *lastChild, **allocChild;
	    
		// get childs of the project
		sdaiGetAttrBN(m_rootObject.id, "IsDecomposedBy", sdaiAGGR, &relaggregates); 
		int nrRelaggregates = sdaiGetMemberCount((int*)relaggregates);

		allocChild = &m_rootObject.child;
		for (int i = 0; i < nrRelaggregates; i++)
		{
			engiGetAggrElement((int*)relaggregates, i, sdaiINSTANCE, &relaggregate);
			sdaiGetAttrBN(relaggregate, "RelatedObjects", sdaiAGGR, &objects);

			int nrObjects = sdaiGetMemberCount((int*)objects);
			if (nrObjects > 0)
				*allocChild = new SIfcObject[nrObjects];

			lastChild = *allocChild;
			for (int n = 0; n < nrObjects; n++)
			{
				engiGetAggrElement((int*)objects, n, sdaiINSTANCE, &lastChild->id);
				sdaiGetAttrBN(lastChild->id, "Name", sdaiSTRING, &lastChild->name);       
				lastChild->type = GetObjectType(&lastChild->id);
				lastChild->primitiveCount = 0;
				lastChild->startIndex = 0;
				lastChild->startVertex = 0;
				lastChild->next = NULL;
				lastChild->child = NULL;

				GetObjects(lastChild);

				if (n < nrObjects - 1)
				{
					lastChild->next = &m_rootObject.child[n + 1]; 
					lastChild = lastChild->next;
				}
			}
			allocChild = &lastChild->next;
		}
	}
}

*/




CGmlModel::CGmlModel()
{
	TRACE("CIfcModel::CIfcModel");
	m_ref       = 0; 
	m_URI       = NULL;
    m_filePath  = NULL;
    m_fileName  = NULL;
	m_type      = MT_GML;
	m_p3DObject = NULL; 
	
    m_minBB.x   = 0.0;
    m_minBB.y   = 0.0;
    m_minBB.z   = 0.0;
    m_maxBB.x   = 0.0;
    m_maxBB.y   = 0.0;
    m_maxBB.z   = 0.0;

	m_nrIndices  = 0;
	m_nrVertices = 0;
	m_pIndices   = NULL;
	m_pVertices  = NULL;
	m_pTexMap    = NULL;
}


void CGmlModel::OpenModel()
{
	TRACE("CGmlModel::OpenModel");

	m_ref = (int) citygml::load(m_URI, citygml::COT_All);
	Add3DObjects();
}


void CGmlModel::CloseModel()
{
	TRACE("CGmlModel::CloseModel");

	if (m_URI != NULL)
		delete[] m_URI;
    if(m_filePath != NULL)
		delete[] m_filePath;
    if(m_fileName != NULL)
		delete[] m_fileName;
	if (m_p3DObject != NULL)
	{
		delete[] m_p3DObject->polygon;
		delete[] m_p3DObject;
	}

	if (m_pIndices != NULL)
		delete[] m_pIndices;
	if (m_pVertices != NULL)
		delete[] m_pVertices;
	if (m_pTexMap != NULL)
		delete[] m_pTexMap;
    
	delete (citygml::CityModel*) m_ref;
}

void CGmlModel::GetBoundingBox()
{
	// determine the object minimum and maximum
	if (m_nrVertices > 0)
	{
		m_minBB.x = m_maxBB.x = (((CUSTOMVERTEX *) m_pVertices)[0]).x; 
		m_minBB.y = m_maxBB.y = (((CUSTOMVERTEX *) m_pVertices)[0]).y; 
		m_minBB.z = m_maxBB.z = (((CUSTOMVERTEX *) m_pVertices)[0]).z; 
		for (int i = 1; i < m_nrVertices; i++)
		{
			if ((((CUSTOMVERTEX *) m_pVertices)[i]).x > m_maxBB.x)      m_maxBB.x = (((CUSTOMVERTEX *) m_pVertices)[i]).x; 
			else if ((((CUSTOMVERTEX *) m_pVertices)[i]).x < m_minBB.x) m_minBB.x = (((CUSTOMVERTEX *) m_pVertices)[i]).x; 
			if ((((CUSTOMVERTEX *) m_pVertices)[i]).y > m_maxBB.y)      m_maxBB.y = (((CUSTOMVERTEX *) m_pVertices)[i]).y; 
			else if ((((CUSTOMVERTEX *) m_pVertices)[i]).y < m_minBB.y) m_minBB.y = (((CUSTOMVERTEX *) m_pVertices)[i]).y; 
			if ((((CUSTOMVERTEX *) m_pVertices)[i]).z > m_maxBB.z)      m_maxBB.z = (((CUSTOMVERTEX *) m_pVertices)[i]).z; 
			else if ((((CUSTOMVERTEX *) m_pVertices)[i]).z < m_minBB.z) m_minBB.z = (((CUSTOMVERTEX *) m_pVertices)[i]).z; 
		}
	}
}


void CGmlModel::Add3DObjects()
{
	TRACE("CGmlModel::GetGmlData");

	bool first = true;
	m_nrVertices = 0;
	m_nrIndices = 0;
	int nrObjects = 0;
	int nrPolygons = 0;

	citygml::CityModel *city_model = (citygml::CityModel*) m_ref;
	const citygml::CityObjects *city_object = city_model->getCityObjectsByType(citygml::COT_Building);

	const citygml::CityObjectsMap& cityObjectsMap = ((citygml::CityModel*) m_ref)->getCityObjectsMap();
	citygml::CityObjectsMap::const_iterator it = cityObjectsMap.begin();
	for ( ; it != cityObjectsMap.end(); it++ )
	{
		const citygml::CityObjects& v = it->second;
		for ( unsigned int i = 0; i < v.size(); i++ )
		{
			int tmp = nrPolygons;
			const citygml::CityObject* object = v[i];

			for (int n = 0; n < object->size(); n++)
			{
				const citygml::Geometry& g = *object->getGeometry(n);
				for (int k = 0; k < g.size(); k++)
				{
					const citygml::Polygon *p = g[k];
					if ( !p || !p->getIndices() || p->getIndicesSize() == 0 ) continue;

					nrPolygons++;
					m_nrVertices += p->size();
					m_nrIndices += p->getIndicesSize();
				}
			}
			if (nrPolygons > tmp)
				nrObjects++; //count only the objects that have geometry (polygons)
		}
	}

	m_pIndices  = new int[m_nrIndices];
	m_pVertices = new CUSTOMVERTEX[m_nrVertices];
	m_pTexMap   = new VECTOR2f[m_nrVertices];
	m_p3DObject = new S3DObject[nrObjects];
	SPolygon  *polygons = new SPolygon[nrPolygons];
	int ni = 0;
	int nv = 0;
	int no = 0;
	int np = 0;

	float dif[4], emi[4], spe[4];
	CTexture *texture;

	it = cityObjectsMap.begin();
	for ( ; it != cityObjectsMap.end(); it++ )
	{
		const citygml::CityObjects& v = it->second;
		for ( unsigned int i = 0; i < v.size(); i++ )
		{
			const citygml::CityObject* object = v[i];
			for (int n = 0; n < object->size(); n++)
			{
				const citygml::Geometry& g = *object->getGeometry(n);
				for (int k = 0; k < g.size(); k++)
				{
					const citygml::Polygon *p = g[k];

					if ( !p || !p->getIndices() || p->getIndicesSize() == 0 ) continue;

					ASSERT(np < nrPolygons);
					if (m_p3DObject[no].polygon == NULL) 
						m_p3DObject[no].polygon = &(polygons[np]);
					else
						polygons[np - 1].next = &(polygons[np]);
						
					polygons[np].primitiveCount = p->getIndicesSize() / 3; 
					polygons[np].startIndex = ni;
					polygons[np].startVertex = nv;

					const citygml::Appearance *mat = p->getAppearance();
					const citygml::TexCoords *texCoords = NULL;

					// add material (possibly containing a texture)
					polygons[np].mtrl = 0;
					if (mat)
					{
						bool hasMaterial = false;
						if ( const citygml::Material* m = dynamic_cast<const citygml::Material*> (mat) )
						{
							TVec3f tmp;
							tmp = m->getDiffuse();
							dif[0] = tmp.r;  dif[1] = tmp.g;  dif[2] = tmp.b;  dif[3] = m->getTransparency();
							tmp = m->getEmissive();
							emi[0] = tmp.r;  emi[1] = tmp.g;  emi[2] = tmp.b;  emi[3] = dif[3];
							tmp = m->getSpecular();
							spe[0] = tmp.r;  spe[1] = tmp.g;  spe[2] = tmp.b;  spe[3] = dif[3];
							polygons[np].mtrl = gMaterialManager.AddMaterial(dif, dif, emi, spe);
						}
						else if ( const citygml::Texture* t = dynamic_cast<const citygml::Texture*> (mat) ) 
						{
							CString url = m_filePath;
							url += t->getUrl().c_str();
							texCoords = p->getTexCoords();
							texture = gTextureManager.AddTexture(url);	
							polygons[np].mtrl = gMaterialManager.AddMaterial(texture);
						}
					}
					polygons[np].next = NULL;
					np++;

					// add indices
					int offset = nv;
					for (int l = 0; l < p->getIndicesSize(); l++)
					{
						ASSERT (ni < m_nrIndices);
						m_pIndices[ni] = offset + p->getIndices()[l];
						ni++;
					}

					// add vertices (including the texture vertices)
					for (int l = 0; l < p->size(); l++)
					{
						ASSERT (nv < m_nrVertices);
						((CUSTOMVERTEX *)m_pVertices)[nv].x = (*p)[l][0];
						((CUSTOMVERTEX *)m_pVertices)[nv].y = (*p)[l][1];
						((CUSTOMVERTEX *)m_pVertices)[nv].z = (*p)[l][2];
						((CUSTOMVERTEX *)m_pVertices)[nv].nx = p->getNormal().x;
						((CUSTOMVERTEX *)m_pVertices)[nv].ny = p->getNormal().y;
						((CUSTOMVERTEX *)m_pVertices)[nv].nz = p->getNormal().z;

						if (texCoords)
						{
							m_pTexMap[nv].x = (*texCoords)[l].x;
							m_pTexMap[nv].y = (*texCoords)[l].y;	
						}
						nv++;
					}
				}
			}
			if (m_p3DObject[no].polygon)
			{
				ASSERT (no < nrObjects);
				m_p3DObject[no].ref = (int) object;
				m_p3DObject[no].name = (char*) object->getId().c_str();
//				m_p3DObject[no].polygon = NULL;
				if (no + 1 < nrObjects)
					m_p3DObject[no].next = &(m_p3DObject[no + 1]);
				else
					m_p3DObject[no].next = NULL;
				no++;
			}
		}
	}
}
